package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.content.model.dto.CourseBaseDTO;
import com.xuecheng.api.content.model.dto.CoursePubDTO;
import com.xuecheng.api.content.model.dto.TeachplanDTO;
import com.xuecheng.api.system.model.dto.CourseCategoryDTO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.CourseModeEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.common.util.JsonUtil;
import com.xuecheng.content.agent.SystemApiAgent;
import com.xuecheng.content.agent.TeachingApiAgent;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.common.constant.CoursePubTemplateKey;
import com.xuecheng.content.convert.CourseBaseConvert;
import com.xuecheng.content.convert.CoursePubConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.CourseMarket;
import com.xuecheng.content.entity.CoursePub;
import com.xuecheng.content.entity.CoursePubMsg;
import com.xuecheng.content.entity.TeachplanWork;
import com.xuecheng.content.mapper.CoursePubMapper;
import com.xuecheng.content.service.CourseBaseService;
import com.xuecheng.content.service.CourseMarketService;
import com.xuecheng.content.service.CoursePubMsgService;
import com.xuecheng.content.service.CoursePubService;
import com.xuecheng.content.service.TeachplanService;
import com.xuecheng.content.service.TeachplanWorkService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 课程发布 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class CoursePubServiceImpl extends ServiceImpl<CoursePubMapper, CoursePub> implements CoursePubService {

    @Autowired
    private CourseBaseService courseBaseService;

    @Autowired
    private CourseMarketService courseMarketService;

    @Autowired
    private TeachplanService teachplanService;

    @Autowired
    private SystemApiAgent systemApiAgent;

    @Autowired
    private CoursePubMsgService coursePubMsgService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private TeachingApiAgent teachingApiAgent;

    @Autowired
    private TeachplanWorkService teachplanWorkService;


    @Autowired
    private CoursePubService coursePubService;
    /*
     * 主方法业务：
     *   1.构建CoursePub数据并保存
     *
     *   2.根据CoursePub内容构架DataMap数据
     *
     *   3.将DataMap进行返回
     * */
    @Transactional
    public Map<String, Object> preview(Long courseId, Long companyId) {

        //1.构建CoursePub数据并保存
        CoursePub coursePub = generateCoursePub(courseId, companyId, false);


        // 2.根据CoursePub内容构架DataMap数据
        Map<String, Object> dataMap = generateDataMap(coursePub);

        // 3.将DataMap进行返回
        return dataMap;
    }


    private Map<String, Object> generateDataMap(CoursePub coursePub) {

        // 1.构建数据模型对象
        HashMap<String, Object> dataMap = new HashMap<>();

        // 2.构建coursePub
        dataMap.put(CoursePubTemplateKey.COURSEPUB, coursePub);

        // 3.构建courseMarket
        String marketJsonString = coursePub.getMarket();
        CourseMarket courseMarket = JsonUtil.jsonToObject(marketJsonString, CourseMarket.class);

        dataMap.put(CoursePubTemplateKey.COURSEMARKET, courseMarket);

        // 4.构建课程计划
        String teachplanJsonString = coursePub.getTeachplan();
        TeachplanDTO teachplanDTO = JsonUtil.jsonToObject(teachplanJsonString, TeachplanDTO.class);
        dataMap.put(CoursePubTemplateKey.TEACHPLANNODE, teachplanDTO);

        // 5.构建课程模式
        CourseModeEnum[] values = CourseModeEnum.values();
        dataMap.put(CoursePubTemplateKey.COURSETEACHMODEENUMS, values);

        return dataMap;
    }


    /*
     * 构建CoursePub数据并保存
     *   业务分析：
     *       1.判断关键数据
     *        courseId companyId
     *       2.判断业务数据
     *           课程基础信息
     *               判断是否存在
     *               判断是否是同一家机构
     *               判断是否删除
     *               判断审核状态：教学机构课程预览--未提交、审核未通过
     *           课程营销
     *               判断是否存在：根据courseid
     *           课程计划
     *               获得课程计划：根据courseId和companyId（树形结构）
     *           课程教师
     *               判断教师信息是否存在：一定要确保课程最少有一个教师信息
     *
     *           课程分类数据并完善Coursepub数据
     *               调用system服务获得课程分类的名称
     *
     *       3.保存课程发布数据
     *           CoursePub数据保存数据库中
     *               课程基础信息  课程营销  课程计划  课程教师
     *
     *       4.将coursePub数据返回
     *
     * */
    private CoursePub generateCoursePub(Long courseId, Long companyId, boolean isPublish) {

        //1.判断关键数据
        //  courseId companyId

        // 2.判断业务数据
        //     课程基础信息
        //         判断是否存在
        //         判断是否是同一家机构
        //         判断是否删除
        //         判断审核状态：教学机构课程预览--未提交、审核未通过
        CourseBaseDTO courseBase = courseBaseService.getCourseBaseById(courseId, companyId);

        String auditStatus = courseBase.getAuditStatus();
        if (isPublish) {

            if (!(CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus))) {
                ExceptionCast.cast(ContentErrorCode.E_120201);
            }


        } else {
            if (CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(auditStatus) ||
                    CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(auditStatus) ||
                    CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(auditStatus)
            ) {
                ExceptionCast.cast(ContentErrorCode.E_120015);
            }
        }

        //     课程营销
        //         判断是否存在：根据courseid  (courseBaseService.getCourseBaseById 里已经对其进行判断是否存在)
        LambdaQueryWrapper<CourseMarket> marketQueryWrapper = new LambdaQueryWrapper<>();
        marketQueryWrapper.eq(CourseMarket::getCourseId, courseId);

        CourseMarket courseMarket = courseMarketService.getOne(marketQueryWrapper);


        //     课程计划
        //         获得课程计划：根据courseId和companyId（树形结构）
        TeachplanDTO teachplanTreeNodes = teachplanService.getTreeNodes(courseId, companyId);


        //     课程教师
        //         判断教师信息是否存在：一定要确保课程最少有一个教师信息
        // TODO: 2021/12/17 学员完成


        //     课程分类数据并完善Coursepub数据
        //         调用system服务获得课程分类的名称
        String mt = courseBase.getMt();
        String st = courseBase.getSt();

        RestResponse<CourseCategoryDTO> mtResponse = systemApiAgent.getCourseCategoryById4s(mt);

        if (!(mtResponse.isSuccessful())) {
            ExceptionCast.castWithCodeAndDesc(mtResponse.getCode(), mtResponse.getMsg());
        }

        RestResponse<CourseCategoryDTO> stResponse = systemApiAgent.getCourseCategoryById4s(st);

        if (!(stResponse.isSuccessful())) {
            ExceptionCast.castWithCodeAndDesc(stResponse.getCode(), stResponse.getMsg());
        }

        CourseCategoryDTO mtEntity = mtResponse.getResult();
        CourseCategoryDTO stEntity = stResponse.getResult();


        // 构建coursePub数据并执行保存操作
        // 课程基础数据
        CoursePub coursePub = CoursePubConvert.INSTANCE.courseBase2coursePub(courseBase);

        // 课程营销数据
        String marketJsonString = JsonUtil.objectTojson(courseMarket);
        coursePub.setMarket(marketJsonString);
        coursePub.setPrice(courseMarket.getPrice());
        coursePub.setCharge(courseMarket.getCharge());

        // 课程计划数据
        String teachplanJsonString = JsonUtil.objectTojson(teachplanTreeNodes);
        coursePub.setTeachplan(teachplanJsonString);

        // 课程教师
        // TODO: 2021/12/17 学员完成


        // 课程分类数据
        coursePub.setMtName(mtEntity.getName());
        coursePub.setStName(stEntity.getName());


        // 3.保存课程发布数据
        //     CoursePub数据保存数据库中
        //         课程基础信息  课程营销  课程计划  课程教师
        // 一个courseBase数据对应一个coursePub数据
        LambdaQueryWrapper<CoursePub> pubQueryWrapper = new LambdaQueryWrapper<>();
        pubQueryWrapper.eq(CoursePub::getCourseId, courseId);
        pubQueryWrapper.eq(CoursePub::getCompanyId, companyId);

        CoursePub po = this.getOne(pubQueryWrapper);

        boolean result = false;
        // 判断课程发布数据是否存在
        if (ObjectUtils.isEmpty(po)) {
            //     如果不存在
            //             创建课程发布数据
            //     创建课程发布数据时，要将coursePub和courseBase数据进行关联
            coursePub.setCourseId(courseBase.getCourseBaseId());
            result = this.save(coursePub);

        } else {
            //     如果存在
            //             修改课程发布数据内容
            coursePub.setId(po.getId());

            result = this.updateById(coursePub);

        }

        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120205);
        }

        //创建coursePub发布数据时生成courseWorkRec记录
        CoursePubDTO coursePubDTO = CoursePubConvert.INSTANCE.entity2dto(coursePub);

        //统计课程绑定作业的数量
        LambdaQueryWrapper<TeachplanWork> teachplanWorkQueryWrapper = new LambdaQueryWrapper<>();
        teachplanWorkQueryWrapper.eq(TeachplanWork::getCourseId, coursePubDTO.getCourseId());
        Integer count = teachplanWorkService.count(teachplanWorkQueryWrapper);

        teachingApiAgent.createCourseWorkRecByCourseId(coursePubDTO,count);

        // 4.将coursePub数据返回
        return coursePub;
    }


    @Value("${course.publish.exchange}")
    private String exchange;
    @Value("${course.publish.routingkey}")
    private String routingkey;

    /*
    * 业务分析：
    *   1.判断关键数据
            courseBaseId companyId
    *   2.判断业务数据
    *       课程基础信息
    *           判断是否存在
    *           判断是否是同一家机构
    *           判断是否删除
    *           判断审核状态
    *               必须是：审核通过
    *   3.保存数据的业务数据和消息数据
    *       业务数据：CoursePub数据进行保存
    *       消息数据：CoursePubMsg数据进行保存
    *   4.发送消息给MQ
    *       指定：交换机名称、routingkey、发送消息CoursePubMsg数据、CorrelationData指定成功和失败方法
    * */
    @Transactional
    public void publish(Long courseBaseId, Long companyId, boolean isPublish) {
        // 获得课程发布消息标识变量
        CoursePubMsg coursePubMsg = null;
        if (!isPublish) {
            //1.判断关键数据
            //     courseBaseId companyId
            // 2.判断业务数据
            //     课程基础信息
            //         判断是否存在
            //         判断是否是同一家机构
            //         判断是否删除
            //         判断审核状态
            //             必须是：审核通过
            // 3.保存数据的业务数据和消息数据
            //     业务数据：CoursePub数据进行保存
            CoursePub coursePub = this.generateCoursePub(courseBaseId, companyId, true);

            //     消息数据：CoursePubMsg数据进行保存
            // 为了保证前端操作时，数据库中的消息表数据不会出现唯一约束的问题，需要在添加消息数据前查询是否有数据
            //         如果有不添加
            //         如果没有添加数据
            LambdaQueryWrapper<CoursePubMsg> msgQueryWrapper = new LambdaQueryWrapper<>();
            msgQueryWrapper.eq(CoursePubMsg::getPubId, coursePub.getId());
            coursePubMsg = coursePubMsgService.getOne(msgQueryWrapper);
            if (ObjectUtils.isEmpty(coursePubMsg)) {
                coursePubMsg = getCoursePubMsg(courseBaseId, companyId, coursePub);
                boolean result = coursePubMsgService.save(coursePubMsg);
                if (!result) {
                    ExceptionCast.cast(ContentErrorCode.E_120206);
                }
            }
        } else {

            // 1.获得课程发布信息
            LambdaQueryWrapper<CoursePub> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(CoursePub::getCourseId, courseBaseId);
            queryWrapper.eq(CoursePub::getCompanyId, companyId);
            CoursePub coursePub = this.getOne(queryWrapper);

            // 2.构建CoursePubMsg数据
            coursePubMsg = getCoursePubMsg(courseBaseId, companyId, coursePub);

        }


        // TODO 4.发送消息给MQ
        //     指定：交换机名称、routingkey、发送消息CoursePubMsg数据、CorrelationData指定成功和失败方法
        /*
         * 参数：
         *   1.交互机名称
         *   2.routingkey名称
         *   3.发送的消息
         *   4.CorrelationData指定成功和失败方法
         * */
        // 将消息转换为JSON对象
        // 要求:重新 rabbittemplate ack机制 指定消息的id值（消息唯一标识）
                // ack=true 交互机响应成功
                    // ack 交换机成功接收消息
                    // 交换机成功接受消息后，要修改本地的数据状态  execuChangeLocalStatusData方法
                // ack=false  交互机响应失败,记录错误日志
    }

    private CoursePubMsg getCoursePubMsg(Long courseBaseId, Long companyId, CoursePub coursePub) {
        CoursePubMsg coursePubMsg;
        coursePubMsg = new CoursePubMsg();
        coursePubMsg.setPubId(coursePub.getId());
        coursePubMsg.setPubName(coursePub.getName());
        coursePubMsg.setPubStatus(CoursePubMsg.UNSENT);
        coursePubMsg.setCourseId(courseBaseId);
        coursePubMsg.setCompanyId(companyId);
        return coursePubMsg;
    }

    /*
    * 修改本地数据的状态值：
    *       1.CourseBase
    *           审核状态：课程已发布
    *       2.CoursePubMsg
    *           发送成功的状态：0->1
    * */
    public void execuChangeLocalStatusData(String courseBaseId) {

        // 1.修改课程基础信息中的审核状态
        LambdaUpdateWrapper<CourseBase> baseUpdateWrapper = new LambdaUpdateWrapper<>();

        baseUpdateWrapper.set(CourseBase::getAuditStatus, CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode());
        baseUpdateWrapper.set(CourseBase::getChangeDate, LocalDateTime.now());
        Long courseid = new Long(courseBaseId);
        baseUpdateWrapper.eq(CourseBase::getId, courseid);

        boolean baseResult = courseBaseService.update(baseUpdateWrapper);

        if (!baseResult) {
            log.error("课程发布修改课程审核状态失败：courseBaseID: {}", courseBaseId);
        }

        // 2.修改发布消息表中的状态
        LambdaUpdateWrapper<CoursePubMsg> msgUpdateWrapper = new LambdaUpdateWrapper<>();
        msgUpdateWrapper.set(CoursePubMsg::getPubStatus, CoursePubMsg.SENT);
        msgUpdateWrapper.eq(CoursePubMsg::getCourseId, courseid);

        boolean msgResult = coursePubMsgService.update(msgUpdateWrapper);

        if (!msgResult) {
            log.error("课程发布修改消息状态失败：courseBaseID: {}", courseBaseId);
        }
    }

    //远程调用-课程内容管理服务-根据课程名称查找coursePub的id
    public RestResponse<CoursePubDTO> getByCourseName4s(String courseName) {
        if (ObjectUtils.isEmpty(courseName)) {
            RestResponse.validfail(CommonErrorCode.E_100101.getDesc());
        }
        LambdaQueryWrapper<CoursePub> coursePubQueryWrapper = new LambdaQueryWrapper<>();
        coursePubQueryWrapper.eq(CoursePub::getName, courseName);
        CoursePub coursePub = this.getOne(coursePubQueryWrapper);
        CoursePubDTO coursePubDTO = CoursePubConvert.INSTANCE.entity2dto(coursePub);
        return RestResponse.success(coursePubDTO);
    }
    @Override
    public CoursePubDTO getCourseBaseByIdNoCompanyId(Long courseBaseId) {
        LambdaQueryWrapper<CoursePub> baseQueryWrapper = new LambdaQueryWrapper<>();
        baseQueryWrapper.eq(CoursePub::getId, courseBaseId);
        // baseQueryWrapper.eq(CourseBase::getStatus, CommonEnum.USING_FLAG.getCodeInt());

        CoursePub coursePub = coursePubService.getOne(baseQueryWrapper);

        if (ObjectUtils.isEmpty(coursePub)) {
            throw new RuntimeException("课程不存在");
        }

        Integer status = coursePub.getStatus();
        if (!(CommonEnum.USING_FLAG.getCodeInt().equals(status))) {
            throw new RuntimeException("课程信息已经被删除");
        }
        CoursePubDTO courseBaseDTO = CoursePubConvert.INSTANCE.entity2dto(coursePub);

        return courseBaseDTO;
    }


    public RestResponse<CourseBaseDTO> getCourseBaseByTargetId(Long targetId) {
        //获取课程发布信息
//        LambdaQueryWrapper<CoursePubMsg> queryWrapper = new LambdaQueryWrapper<>();
//        queryWrapper.eq(CoursePubMsg::getPubId, targetId);
        CoursePub coursePub = this.getById(targetId);
         Long id = coursePub.getCourseId();
        CourseBase courseBase = courseBaseService.getById(id);
        CourseBaseDTO courseBaseDTO = CourseBaseConvert.INSTANCE.entity2dto(courseBase);
        return RestResponse.success(courseBaseDTO);
    }

    @Override
    public RestResponse<List<String>> getCoursePubByWorkId4s(Long workId) {
        LambdaQueryWrapper<TeachplanWork> query = Wrappers.lambdaQuery(TeachplanWork.class);
        query.eq(TeachplanWork::getWorkId,workId);
        List<TeachplanWork> list = teachplanWorkService.list(query);
        ArrayList<String> arrayList = new ArrayList<>();
        for (TeachplanWork teachplanWork : list) {
            CourseBase courseBase = courseBaseService.getById(teachplanWork.getCourseId());
            arrayList.add(courseBase.getName());
        }
        return RestResponse.success(arrayList);
    }
}
